/*
 * Copyright (c) 2010 Mathias Garbe <code@mathias-garbe.de>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <QtGui>
#include "client.hpp"
#include "MainWindow.hpp"

MainWindow::MainWindow(QWidget *parent, Qt::WFlags flags)
	: QMainWindow(parent, flags)
{
	ui.setupUi(this);

    // Create tray icon
    this->trayIcon = new QSystemTrayIcon(this);
    this->trayIcon->setContextMenu(this->ui.menuFile);
    QIcon icon(":/misc/icon.ico");
    trayIcon->setIcon(icon);

    //connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(messageClicked()));
    connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
            this, SLOT(systrayActivated(QSystemTrayIcon::ActivationReason)));
    trayIcon->show();

    this->curGameID = -1;

    this->refreshGames();

    this->onlineStatus = new OnlineStatus();

    // Setup onlinePlayers treeView
    this->ui.onlinePlayers->setRootIsDecorated(false);
    this->ui.onlinePlayers->setAlternatingRowColors(true);
    this->model = new QStandardItemModel(0, 3, parent);
    this->model->setHeaderData(0, Qt::Horizontal, QObject::tr("Nickname"));
    this->model->setHeaderData(1, Qt::Horizontal, QObject::tr("Status"));
    this->model->setHeaderData(2, Qt::Horizontal, QObject::tr("Country"));
    this->ui.onlinePlayers->setModel(this->model);

    this->ui.onlinePlayers->setColumnWidth(0, 100);
    this->ui.onlinePlayers->setColumnWidth(1, 50);
    this->ui.onlinePlayers->setColumnWidth(2, 65);

    // Connect menu items to a slot
    connect(this->ui.actionStart_Tiberian_Dawn, SIGNAL(triggered()), this, SLOT(startTD()));
    connect(this->ui.actionStart_Red_Alert, SIGNAL(triggered()), this, SLOT(startRA()));
    connect(this->ui.actionStart_Tiberian_Sun, SIGNAL(triggered()), this, SLOT(startTS()));
    connect(this->ui.actionStart_Red_Alert_2, SIGNAL(triggered()), this, SLOT(startRA2()));
    connect(this->ui.actionStart_Red_Alert_2_Yuris_Revenge, SIGNAL(triggered()), this, SLOT(startRA2YR()));
    connect(this->ui.actionQuit, SIGNAL(triggered()), this, SLOT(close()));
    connect(this->ui.actionPreferences, SIGNAL(triggered()), this, SLOT(openSettings()));
    connect(this->ui.actionCheck_for_updates, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
    connect(this->ui.actionRefresh_games, SIGNAL(triggered()), this, SLOT(menuRefreshGames()));
	connect(this->ui.actionAbout, SIGNAL(triggered()), this, SLOT(aboutBox()));
	connect(this->ui.actionOpen_Online_Help, SIGNAL(triggered()), this, SLOT(openOnlineHelp()));
	connect(this->ui.actionOpen_CnCNet_Forums, SIGNAL(triggered()), this, SLOT(openForums()));
	connect(this->ui.actionOpen_Chatlounge_IRC, SIGNAL(triggered()), this, SLOT(openIRC()));

    // Delegate WebBrowser clicks to new standard browser window
    this->ui.webNews->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
    this->ui.webNews->load(QUrl("http://cncnet.cnc-comm.com/tinynews.php"));
    connect(this->ui.webNews, SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl)));

    //this->ui.webChat->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
    //this->ui.webChat->load(QUrl("http://webchat.freenode.net/?channels=cncnet"));
    //connect(this->ui.webChat, SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl)));
    // the freenode webchat is bugged :( - lets remove the tab for now
    this->ui.tabWidget->removeTab(1);

    // also the ingame tab is not working yet, lets remove it also
    this->ui.tabWidget->removeTab(1);

    // Chat and Website label click
    //this->ui.labelIrc->setOpenExternalLinks(true);
    this->ui.labelCncnet->setOpenExternalLinks(true);

    // Setting up windows
    this->settingsWin = new SettingsWindow(this, 0);
    this->gameWin = new GameWindow(this, 0);
    this->gameSelectWin = new GameSelectWindow(this, 0);

    this->incomingPackets = new ThreadingVector<Packet>();
    this->outgoingPackets = new ThreadingVector<Packet>();

    this->session = 0;
    this->checkIrc();
    this->rebuildTabs();


    //this->ui.textIrcChat->setStyleSheet("QTextEdit { font: 10pt Tahoma, Verdana, Arial, Helvetica; background-color: rgb(0, 0, 0); color: rgb(84, 176, 36); }");
    this->ui.textIrcChat->setStyleSheet("QTextEdit { font: 10pt Tahoma, Verdana, Arial, Helvetica; background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); }");

    connect(this->ui.lineIrcMessage, SIGNAL(returnPressed()), this, SLOT(onSendIrcMessage()));
}


MainWindow::~MainWindow()
{
    delete this->settingsWin;
    delete this->gameWin;
    delete this->gameSelectWin;   

    delete this->incomingPackets;
    delete this->outgoingPackets;

    if(this->session != 0)
    {
        this->session->disconnectFromServer();
        Sleep(200);
        delete this->session;
    }
}

void MainWindow::show()
{
    // Move window to the middle of the screen
    QRect screen = QApplication::desktop()->screenGeometry();
    this->move(screen.width()/2 - this->width()/2, screen.height()/2 - this->height()/2);
    QMainWindow::show();

    this->closed = false;

    QSettings settings;
    bool firstStart = settings.value("General/FirstStart", true).toBool();

    if(firstStart)
    {
        this->openSettings();
    }

    this->rebuildTabs();

    int t = 0;
    while(!this->closed)
    {
        Qt::QAppInstance()->processEvents(QEventLoop::AllEvents, 20);
        Qt::QAppInstance()->flush();

        if(!this->gameWin->isIngame() && !this->gameWin->closed)
        {
            this->gameWin->close();
            this->setWindowTitle("CnCNet");

            // Make online status update.
            t = 0;
        }

        Sleep(20);

        if(t+15 < time(0))
        {
            t = time(0);
            this->updateOnlineStatus();
        }

        // ITC stuff
        #ifdef _DEBUG___foo

        std::vector<Packet> data = this->incomingPackets->getArrivedData();

        int i = 0;
        for(std::vector<Packet>::iterator iter = data.begin(); iter != data.end(); iter++)
        {
            Packet packet  = (*iter);
            if(packet.getParsedType() == ChatMessage)
            {
                std::string line = "<";
                line.append(packet.getChatNick().append("> ").append(packet.getChatMessage()));

                std::cout << "Chat: " << line << std::endl;
            }
            /*FILE *fp;
            fp=fopen("packetdump2.txt", "ab+");
            fwrite(packet.getMessage(), 1, packet.getMessageLength(), fp);
            fclose(fp);*/
        }
        #endif
    }
}

void MainWindow::refreshGames()
{
    this->logo.load(":/misc/gcode-logo.png", 0);
    this->gameImg[0].load(":/misc/TD", 0);
    this->gameImg[1].load(":/misc/RA", 0);
    this->gameImg[2].load(":/misc/TS", 0);
    this->gameImg[3].load(":/misc/RA2", 0);
    this->gameImg[4].load(":/misc/YR", 0);

    QSettings settings;

    for(int i=0;i<5;i++)
    {
        std::stringstream tmpCount;
        tmpCount << "CustomPath_Advanced_" << Games::getGameString(i) << "/Count";
        int count = settings.value(tmpCount.str().c_str(), 0).toInt();

        if(!settings.value("General/AdvancedSettings", false).toBool())
            count = 0;

        // Convert game logo to monochrome when the game is not available
        if(StaticGameInfo::getGameInfo(i).getExe() == "" && count == 0)
        {
            for (int y = 0; y < gameImg[i].height(); ++y) {
                for (int x = 0; x < gameImg[i].width(); ++x) {
                    int pixel = gameImg[i].pixel(x, y);
                    int gray = qGray(pixel);
                    int alpha = qAlpha(pixel);
                    gameImg[i].setPixel(x, y, qRgba(gray, gray, gray, alpha));
                }
            }
        }
    }

    // Enable/disable menu items
    this->ui.actionStart_Tiberian_Dawn->setEnabled((StaticGameInfo::getGameInfo(0).getExe() != ""));
    this->ui.actionStart_Red_Alert->setEnabled((StaticGameInfo::getGameInfo(1).getExe() != ""));
    this->ui.actionStart_Tiberian_Sun->setEnabled((StaticGameInfo::getGameInfo(2).getExe() != ""));
    this->ui.actionStart_Red_Alert_2->setEnabled((StaticGameInfo::getGameInfo(3).getExe() != ""));
    this->ui.actionStart_Red_Alert_2_Yuris_Revenge->setEnabled((StaticGameInfo::getGameInfo(4).getExe() != ""));

}

void MainWindow::rebuildTabs()
{
    int tab = this->ui.tabWidget->currentIndex();

    // Remove all tabs
    int count = this->ui.tabWidget->count();

    for(int i=0; i<count; i++)
    {
        this->ui.tabWidget->removeTab(0);
    }

    QSettings settings;

    bool enabled = settings.value("IRC/Enabled", true).toBool();

    count = 0;

    this->ui.tabWidget->insertTab(count++, this->ui.tabNews, "News");

    if(enabled)
    {
        this->ui.tabWidget->insertTab(count++, this->ui.tabIrc, "Chat");
    }

    if(this->ui.tabWidget->count() >= tab)
    {
        this->ui.tabWidget->setCurrentIndex(tab);
    }

    if(enabled)
    {
        this->ui.tabWidget->setCurrentIndex(count-1);
    }
}

// Sends current status to the server
// and gets online players list
void MainWindow::updateOnlineStatus()
{
	// Are we currently playing a game?
    int gameID = -1;
    if(this->gameWin->isIngame())
    {
        gameID = this->curGameID;
    }

	// Submit status to server
    this->onlineStatus->updateStatus(gameID);

	// Clear our player list
	// Maybe there is a better way to do this?
    int max = this->model->rowCount();
    for(int i=0; i<max; i++)
    {
        this->model->removeRows(0, 1);
    }

	// Iterate trough all online players and
	// add them to our player list
    std::vector<OnlinePlayer> onlinePlayers = this->onlineStatus->getOnlinePlayers();
    for(unsigned int i=0; i < onlinePlayers.size(); i++)
    {
        int s = this->model->rowCount();
        this->model->insertRow(s);
        this->model->setData(model->index(s, 0), onlinePlayers[i].name.c_str());

		// Idle is the default
        std::string status = "Idle";
        if(onlinePlayers[i].gameID != -1)
        {
            status = Games::getGameNameAbbrString(onlinePlayers[i].gameID);
        }

        this->model->setData(model->index(s, 1), status.c_str());

        this->model->setData(model->index(s, 2), onlinePlayers[i].country.c_str());
    }
}

// Starts a specific game
void MainWindow::startGame(int gameID)
{
    if(this->gameWin->isIngame())
    {
        QMessageBox msgBox;
        msgBox.setWindowTitle("A game is already running");
        msgBox.setText("Please close the running game before starting a new one.");
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.setDefaultButton(QMessageBox::Ok);
        msgBox.setIcon(QMessageBox::Critical);

        int ret = msgBox.exec();
        return;
    }

    QSettings settings;
    bool useAdvancedSettings = settings.value("General/AdvancedSettings", false).toBool();

    std::stringstream tmpCount;
    tmpCount << "CustomPath_Advanced_" << Games::getGameString(gameID) << "/Count";
    int count = settings.value(tmpCount.str().c_str(), 0).toInt();

    if(!settings.value("General/AdvancedSettings", false).toBool())
        count = 0;

    if(StaticGameInfo::getGameInfo(gameID).getExe() == "" && count == 0)
    {
        QMessageBox msgBox;
        std::string msg = Games::getGameNameString(gameID);
        msg.append(" cannot be started because the game cannot be found.\n\n");
        msg.append("If you are sure to have installed it, you can set a custom path for this game in the settings.");

        msgBox.setWindowTitle("Game not found");
        msgBox.setText(msg.c_str());
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.setDefaultButton(QMessageBox::Ok);
        msgBox.setIcon(QMessageBox::Critical);

        int ret = msgBox.exec();
        return;
    }


    GameInfo *gameInfo;
    if(useAdvancedSettings)
    {
        AdvancedGameInfo tmp = this->gameSelectWin->show(gameID);
        gameInfo = new AdvancedGameInfo(tmp.getType(), tmp.getExe(), tmp.getArgs());
    }
    else
    {
        GameInfo tmp = StaticGameInfo::getGameInfo(gameID);
        gameInfo = new AdvancedGameInfo(tmp.getType(), tmp.getExe(), tmp.getArgs());

        if(settings.value("General/ConfirmGameLaunch", true).toBool())
        {
            QMessageBox msgBox;
            std::string msg = "You are going to start ";
            msg.append(Games::getGameNameString(gameID));
            msg.append(", are you sure want to continue?\n\nYou can disable this question in the settings.");

            msgBox.setWindowTitle("Confirm game start");
            msgBox.setText(msg.c_str());
            msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
            msgBox.setDefaultButton(QMessageBox::Yes);
            msgBox.setIcon(QMessageBox::Question);

            int ret = msgBox.exec();
            if(ret == QMessageBox::No)
            {
                return;
            }
        }
    }

    if(gameInfo->getExe() == "")
    {
        return;
    }

    std::stringstream tmp;
    tmp << "CnCNet - " << Games::getGameShortNameString(gameInfo->getType()) << " is running...";
    this->setWindowTitle(tmp.str().c_str());

    this->curGameID = gameID;
    this->onlineStatus->updateStatus(gameID);
    this->gameWin->show(gameInfo, this, this->incomingPackets);

    delete gameInfo;
}

// Window size changed
void MainWindow::changeEvent(QEvent *event)
{
	// Minimize to tray if user wants to
    QSettings settings;
    if(settings.value("General/MinimizeToTray", true).toBool())
    {
        if(event->type() == QEvent::WindowStateChange && isMinimized())
        {
			// This could use improvments
            /*m_minimized_to_tray                      = true;
            m_window_state_before_minimizing_to_tray =
                static_cast<QWindowStateChangeEvent*>(event)->oldState();*/
            QTimer::singleShot(0, this, SLOT(hide()));
        }
    }

	// Process other stuff we are not interessted in.
    QMainWindow::changeEvent(event);
}

// Window is about to be closed
void MainWindow::closeEvent(QCloseEvent *event)
{
    QMessageBox msgBox;
    msgBox.setWindowTitle("CnCNet Client");
    msgBox.setText("Are you sure you want to close the client?");
    msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
    msgBox.setDefaultButton(QMessageBox::No);
    int ret = msgBox.exec();

    if(ret == QMessageBox::No)
    {
        event->ignore();
    }
    else
    {
        this->closed = true;
        event->accept();
    }
}

// Window is repainted
void MainWindow::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setRenderHint(QPainter::SmoothPixmapTransform, true);

    painter.drawImage(10, 18, this->logo, 0, 0, -1, -1);
    
	// Paint all game logos on the window
    for(int i = 0; i<5; i++)
    {
        QRect dest = this->getGameButtonRect(i);
        QRect src(0, 0, 64, 64);

        painter.drawImage(dest, this->gameImg[i], src, Qt::MonoOnly);
    }
}

// Returns the area on the window where a specific
// game logo will be...
QRect MainWindow::getGameButtonRect(int gameID)
{
    return QRect((this->width() - 280) + (gameID * 55), 22, 48, 48);
}

// The user clicked somewhere in the window
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
    int x = event->x();
    int y = event->y();
    int gameClicked = -1;

	// Check if the clicked on a logo
    for(int i=0; i<=5; i++)
    {
        QRect destination = this->getGameButtonRect(i);
        if(x >= destination.x() && x <= destination.x() + destination.width() &&
            y >= destination.y() && y <= destination.y() + destination.height())
        {
            gameClicked = i;
        }
    }

	// if he did, start the game
    if(gameClicked > -1)
    {
        this->startGame(gameClicked);
    }
}

void MainWindow::openSettings()
{
    this->settingsWin->show();
}

void MainWindow::checkForUpdates()
{
    Update *update = new Update();

    QMessageBox msgBox;
    msgBox.setWindowTitle("CnCnet Update");

    int version = update->getBuildVersion();
    if(version <= _BUILD_VERSION)
    {
        msgBox.setText("You already have the latest version, there is no need to update.");
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.setDefaultButton(QMessageBox::Ok);
        msgBox.setIcon(QMessageBox::Information);
        msgBox.exec();
    }
    else
    {
        msgBox.setText("There is a new update available.\nDo you want to update your client?");
        msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
        msgBox.setDefaultButton(QMessageBox::Yes);
        msgBox.setIcon(QMessageBox::Question);
        int ret = msgBox.exec();
        if(ret == QMessageBox::Yes)
        {
            update->doUpdate();
        }
    }
    

    delete update;
}

void MainWindow::checkIrc()
{
    QSettings settings;

    bool enabled = settings.value("IRC/Enabled", true).toBool();
    std::string nick = settings.value("IRC/Nickname", "").toString().toStdString();

    if(nick == "")
    {
        this->addIrcMessage("Please set your IRC nickname in the settings first!");
        return;
    }

    if(this->session == 0)
    {
        if(enabled)
        {
            QStringList channels;
            channels.append("#cncnet");

            this->session = new IrcClient::Session(this);
            session->setNick(nick.c_str());    
            session->setIdent(nick.c_str());
            session->setRealName(nick.c_str());
            session->setAutoJoinChannels(channels);
            session->connectToServer("irc.freenode.net", 6667);
        }
    } 
    else
    {
        if(!enabled)
        {
            int max = this->ui.listIrcNick->count();

            for(int i=0;i<max;i++)
            {
                this->ui.listIrcNick->takeItem(0);
            }

            this->session->disconnectFromServer();
            delete this->session;
            this->session = 0;
        }
    }
}

void MainWindow::onSendIrcMessage()
{
    QString message = this->ui.lineIrcMessage->text();
    message = message.trimmed();

    if(message.indexOf("/") == 0)
    {
        QString command ;
        QString argument;

        int i = message.indexOf(" ");
        if(i == -1)
        {
            i = message.length();
            argument = "";
        }
        else
        {
            argument = message.mid(i+1);
        }

        command = message.left(i);

        if(command == "/me")
        {
            QMetaObject::invokeMethod(this->session, "ctcpAction", Qt::QueuedConnection, Q_ARG(QString, "#cncnet"), Q_ARG(QString, argument));
        }
        else
        {
            QString message = "Unknown command: ";
            message.append(command);
            this->addIrcMessage(message);
        }

    }
    else
    {
        QMetaObject::invokeMethod(this->session, "message", Qt::QueuedConnection, Q_ARG(QString, "#cncnet"), Q_ARG(QString, message));
    }

    this->ui.lineIrcMessage->setText("");
}
